#include<iostream>
#include"func.h"
#include<opencv2/imgcodecs.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/objdetect.hpp>
#include<string>
#include<vector>

using namespace std;
using namespace cv;

void huizhituxiang()
{
    Mat img=Mat::zeros(Size(512,512),CV_8UC3);//定义一个512*512的8位黑色图像用于绘图

    //画圆
    circle(img,Point(50,50),25,Scalar(255,255,255),-1);//画圆函数(要绘制的图像，圆心坐标，半径，BGR颜色，轮廓宽度<负值为实心>)
    circle(img,Point(100,50),20,Scalar(255,255,255),4);

    //画直线
    line(img,Point(100,100),Point(200,100),Scalar(255,255,255),2,LINE_4,0);
    //画直线函数(目标图像，起点坐标，终点坐标，BGR颜色，线宽，边界类型标志，起终点小数点位数)

    //画椭圆
    ellipse(img,Point(300,255),Size(100,70),0,0,100,Scalar(255,255,255),-1);
    //画椭圆函数(目标图，中心点坐标，椭圆两轴大小一半，椭圆旋转角度，椭圆弧起始角度，椭圆弧终止角度，BGR颜色，轮廓宽度<负值为实心>)

    //画多边形
    Point pp[2][6];//定义存放两个六边形顶点坐标的数组
    pp[0][0]=Point(72,200);
    pp[0][1]=Point(142,204);
    pp[0][2]=Point(226,263);
    pp[0][3]=Point(172,310);
    pp[0][4]=Point(117,319);
    pp[0][5]=Point(15,260);
    pp[1][0]=Point(359,339);
    pp[1][1]=Point(447,351);
    pp[1][2]=Point(504,349);
    pp[1][3]=Point(484,433);
    pp[1][4]=Point(418,449);
    pp[1][5]=Point(354,402);
    Point pp2[5];//定义存放一个五边形顶点坐标的数组
    pp2[0]=Point(350,83);
    pp2[1]=Point(463,90);
    pp2[3]=Point(500,171);
    pp2[4]=Point(338,141);

    const Point*pts[3]={pp[0],pp[1],pp2};//定义包含数组的数组存放所有多边形顶点坐标
    int npts[]={6,6,5};//定义顶点个数数组
    fillPoly(img,pts,npts,3,Scalar(125,125,125),8);//画多边形函数(目标图，存放所有多边形坐标数组的数组，每个多边形顶点个数数组，多边形个数，BGR颜色，边界类型)

    //生成文字
    putText(img,"puttext",Point(100,400),2,1,Scalar(255,255,255));//生成文字函数(目标图，文字<英文>，字符串左下角坐标，字体类型标志，字体大小，BGR颜色)

    imshow("img",img);
    
    waitKey(0);

}

void ROIquyu()
{
    Mat img=imread("/home/zhanjiabin/桌面/task/study/test2.jpg");
    Mat noobcv=imread("/home/zhanjiabin/桌面/task/study/test2.jpg");

    Mat ROI1,ROI2,ROI2_copy,mask,img2,img_copy;
    resize(noobcv,mask,Size(200,200));
    img2=img;//浅拷贝,img2的矩阵头指向img的数据，img2本身无数据

    img.copyTo(img_copy);//深拷贝，将img数据拷贝至img_copy，img_copy矩阵头指向自身的数据

    Rect rect(206,206,200,200);//定义感兴趣区域 (左上角像素x坐标，右上角像素y坐标，宽度，高度)
    ROI1=img(rect);//在img中按照感兴趣区域截图

    ROI2=img(Range(300,500),Range(300,500));//第二种截图方式 (开始坐标，结束坐标)
    
    img(Range(300,500),Range(300,500)).copyTo(ROI2_copy);//深拷贝指定区域

    mask.copyTo(ROI1); 

    imshow("加入noobcv后的img",img);
    imshow("深拷贝的img_copy",img_copy);
    imshow("ROI对ROI2的影响",ROI2);
    imshow("深拷贝的ROI_copy",ROI2_copy);

    circle(img,Point(300,300),20,Scalar(0,0,255),-1);

    imshow("浅拷贝的img2",img2);
    imshow("画图对ROI1的影响",ROI1);

    waitKey(0);

}

void Guasstuxiangjinzita()
{
    Mat img=imread("/home/zhanjiabin/桌面/task/study/test.jpg");
    vector<Mat>Guass;
    int level=3;//图像金字塔层数

    Guass.push_back(img);//高斯图像金字塔第0层

    for(int i=0;i<level;i++)
    {
        Mat guass;
        pyrDown(Guass[i],guass);//下采样函数(金字塔前一层，输出图像)
        Guass.push_back(guass);//将处理后的图像放入容器
       
    }

    for(int i=0;i<level;i++)
    {
        string name=to_string(i);//to_string函数将整数i转换为字符串表示形式
        imshow(name,Guass[i]); 
    }

    waitKey(0);

}

void lapulasijinzita()
{
    Mat img=imread("/home/zhanjiabin/桌面/task/study/test.jpg");

    vector<Mat>Guass;//定义vector容器存放高斯图像金字塔
    int level=3;

    Guass.push_back(img);//将img尾插入容器中

    for(int i=0;i<level;i++)
    {
        Mat guass;
        pyrDown(Guass[i],guass);//将第i层图像进行缩小和高斯模糊处理
        Guass.push_back(guass);//将处理后的图像尾插放入容器中
    }


    vector<Mat>Lap;//定义vector容器存放拉普拉斯图像金字塔

    for(int i=Guass.size()-1;i>0;i--)//初始i=高斯图像金字塔最顶层
    {
        Mat lap,upGuass;

        if(i==Guass.size()-1)//如果i=高斯金字塔最顶层(最小图像)
        {
            Mat down;
            pyrDown(Guass[i],down);//拉普拉斯金字塔图像是下采样后再上采样的图像与原图的差值
            pyrUp(down,upGuass);//   因此此处需要将最小图片先缩小后放大
            lap=Guass[i]-upGuass;//获取拉普拉斯图像
            Lap.push_back(lap);
        }
        pyrUp(Guass[i],upGuass);//上采样函数(输入图，输出图)
        lap=Guass[i-1]-upGuass;//获取拉普拉斯图像
        Lap.push_back(lap);//将处理后的拉普拉斯图像尾插放入容器中
    }

    for(int i=0;i<Guass.size();i++)
    {
        string name=to_string(i);
        imshow("G"+name,Guass[i]);
        imshow("L"+name,Lap[Guass.size()-1-i]);
    }

    waitKey(0);
    
}

Mat img,imgPoint;

void callBack(int value,void*)//回调函数<用于执行滑动条事件发生后的执行>//(滑动条数值,传递给回调函数的可选参数<这里省略>)                              
{
    float a=value/100.0;

    Mat img2;
    img2=img*a;//和图像和fioat相乘调亮度
    imshow("img",img2);
}

void huadongtiao()
{
    img=imread("/home/zhanjiabin/桌面/task/study/test.jpg");

    namedWindow("img");//创建名为img的窗体
    imshow("img",img);//在img窗体中显示图像

    int valua=100;//定义滑动条初始值为100
    createTrackbar("百分比","img",&valua,600,callBack,0);//创建滑动条函数(滑动条名称，要创建滑动条的窗体，滑动条初始值指针，最大值，回调函数，传递给回调函数的可选参数)
    waitKey(0);
}

Point prePoint;

void mouse(int event,int x,int y,int flags,void*)//鼠标事件回调函数,检测到事件后执行(鼠标相应事件标志(瞬间），鼠标x坐标，鼠标y坐标，鼠标相应事件标志(持续)，传递给回调函数的可选参数<这里省略>)
{
    if(event==EVENT_RBUTTONDOWN)//鼠标右键按下
    {
        cout<<"鼠标左键才可以绘制轨迹"<<endl;
    }
    if(event==EVENT_LBUTTONDOWN)//鼠标左键按下
    {
        prePoint=Point(x,y);//用prePoint存放此时鼠标坐标
        cout<<"轨迹起始坐标"<<prePoint<<endl;
    }
    if(event==EVENT_MOUSEMOVE&&(flags&EVENT_FLAG_LBUTTON))//按住左键移动鼠标
    {
        Point pt(x,y);//定义变量存放初始位置之后的动态鼠标坐标
        line(img,prePoint,pt,Scalar(255,255,255),2,5,0);//根据坐标在img中画线
        prePoint=pt;
        imshow("图像窗口1",img);

        //将鼠标坐标四邻域内的像素变成白色
        imgPoint.at<Vec3b>(y,x)=Vec3b(255,255,255);
        imgPoint.at<Vec3b>(y,x-1)=Vec3b(255,255,255);
        imgPoint.at<Vec3b>(y,x+1)=Vec3b(255,255,255);
        imgPoint.at<Vec3b>(y-1,x)=Vec3b(255,255,255);
        imgPoint.at<Vec3b>(y+1,x)=Vec3b(255,255,255);
        imshow("图像窗口2",imgPoint);
    }

    

}

void shubiaoxiangying()
{
    img = imread("/home/zhanjiabin/桌面/task/study/test.jpg");
    img.copyTo(imgPoint);
    imshow("图像窗口1",img);
    imshow("图像窗口2",imgPoint);
    setMouseCallback("图像窗口1",mouse,0);//鼠标事件检测类型(目标窗口，回调函数，传递给回调函数的参数<一般为0>)
    waitKey(0);
}

void zhifangtu()
{
    Mat img=imread("/home/zhanjiabin/桌面/task/study/test.jpg");
    Mat gray;
    cvtColor(img,gray,COLOR_BGR2GRAY);

    //设置提取值房图的相关变量
    Mat hist;//存放直方图计算结果
    const int channels[1]={0};//图像通道索引，灰度图通道索引为0
    const int bins[1]={256};//直方图维度，就是像素灰值图最大值
    float inRanges[2]={0,255};//所统计图像的灰度值取值范围
    const float* ranges[1]={inRanges};//将inRanges放入数组表示各个图像的灰度值取值范围，此处只有一个图像

    calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges);
  //统计直方图函数(要计算的图像，输入图像数量，图像序号和通道索引，掩码矩阵决定统计区域<所有区域为Mat()>,存放统计结果矩阵，直方图维数<<32>,直方图维度<所统计图像的灰度值取值范围>,存放各个图像的灰度值取值范围的数组)
    
    //准备绘制直方图
    int hist_w=1024;//图像宽度
    int hist_h=768;//图像高度
    int width=2;//设置直方图宽度为两个像素
    Mat histImage=Mat::zeros(hist_h,hist_w,CV_8UC3);//创建宽度为1024高度为768的八位黑色矩阵来画直方图
    // for(int i=1;i<=hist.rows;i++)
    // {
    //     rectangle(histImage,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(hist.at<float>(i-1)/15)),Scalar(255,255,255),-1);
    // }

    Mat hist_INF;
    normalize(hist,hist_INF,1,0,NORM_INF,-1,Mat());//归一化函数(输入图，输出图，归一化上限，归一化下限，归一化标志，输出类型标志，掩码矩阵)最大值变成一
    for(int i=1;i<=hist_INF.rows;i++)
    {
        rectangle(histImage,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(hist_h*hist_INF.at<float>(i-1)-1)),Scalar(255,255,255),-1);
    }
    //是用一种舍入的方法将输入浮点数转换成整数：cvRound()：返回跟参数最接近的整数值，即四舍五入；


    imshow("histImage",histImage);
    imshow("gray",gray);
    waitKey(0);
}

void drawHist(Mat &hist,int type,string name)
{
    int hist_w=512;
    int hist_h=500;
    int width=2;
    Mat histImage=Mat::zeros(hist_h,hist_w,CV_8UC3);
    normalize(hist,hist,1,0,type,-1,Mat());
    for(int i=1;i<=hist.rows;i++)
    {
        rectangle(histImage,Point(width*(i-1),hist_h-1),Point(width*i-1,hist_h-cvRound(hist_h*hist.at<float>(i-1))-1),Scalar(255,255,255),-1);
    }
    imshow(name,histImage);
}

void zhifangtujuhenghua()//将图像像素所在灰度值区间拉大，提高显示效果
{
    Mat img=imread("/home/zhanjiabin/桌面/task/study/test07.jpg");

    Mat gray,hist,hist2;
    cvtColor(img,gray,COLOR_BGR2GRAY);
    Mat equalImg;

    equalizeHist(gray,equalImg);//图像均衡化函数(输入图，输出图)

    const int channels[1]={0};
    float inRanges[2]={0,255};
    const float* ranges[1]={inRanges};
    const int bins[1]={256};

    calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges);
    calcHist(&equalImg,1,channels,Mat(),hist2,1,bins,ranges);

    drawHist(hist,NORM_INF,"hist");
    drawHist(hist2,NORM_INF,"hist2");

    imshow("原图",gray);
    imshow("均衡化后图像",equalImg);
    waitKey(0);

    
}

void capjunheng()
{
    VideoCapture cap(0);

    const int channels[1]={0};
    float inRanges[2]={0,255};
    const float* ranges[1]={inRanges};
    const int bins[1]={256};

    while(1)
    {
        cap.read(img);

        Mat gray,hist,hist2;
        cvtColor(img,gray,COLOR_BGR2GRAY);
        Mat equalImg;

        equalizeHist(gray,equalImg);//图像均衡化函数(输入图，输出图)

        

        calcHist(&gray,1,channels,Mat(),hist,1,bins,ranges);
        calcHist(&equalImg,1,channels,Mat(),hist2,1,bins,ranges);

        drawHist(hist,NORM_INF,"hist");
        drawHist(hist2,NORM_INF,"hist2");

        imshow("原图",gray);
        imshow("均衡化后图像",equalImg);
        waitKey(1);
    }
}

void zhifangtupipei()//将直方图集中在某个区域
{
    Mat img1=imread("/home/zhanjiabin/桌面/task/study/test07.jpg");
    Mat img2=imread("/home/zhanjiabin/桌面/task/study/test.jpg");

    //计算直方图
    Mat hist1,hist2;
    const int channels[1]={0};
    float inRanges[2]={0,255};
    const float* ranges[1]={inRanges};
    const int bins[1]={256};
    calcHist(&img1,1,channels,Mat(),hist1,1,bins,ranges);
    calcHist(&img2,1,channels,Mat(),hist2,1,bins,ranges);

    drawHist(hist1,NORM_L1,"hist1");
    drawHist(hist2,NORM_L1,"hist2");

    //计算两幅图直方图累积概率
    float hist1_cdf[256]={hist1.at<float>(0)};
    float hist2_cdf[256]={hist2.at<float>(0)};
    for(int i=1;i<256;i++)
    {
        hist1_cdf[i]=hist1_cdf[i-1]+hist1.at<float>(i);
        hist2_cdf[i]=hist2_cdf[i-1]+hist2.at<float>(i);
    }

    //构建累积概率误差矩阵
    float diff_cdf[256][256];
    for(int i=0;i<256;i++)
    {
        for(int j=0;j<256;j++)
        {
            diff_cdf[i][j]=fabs(hist1_cdf[i]-hist2_cdf[j]);
        }
    }

    //生成LUT
    Mat lut(1,256,CV_8U);
    for(int i=0;i<256;i++)
    {
        //查找与源灰度值为i的映射灰度
        //与i累积概率误差值最小的规定化灰度
        float min=diff_cdf[i][0];
        int index=0;
        //寻找累积概率误差矩阵中每一行最小值
        for(int j=1;j<256;j++)
        {
            if(min>diff_cdf[i][j])
            {
                min=diff_cdf[i][j];
                index=j;
            }
        }
        lut.at<uchar>(i)=(uchar)index;
    }
    Mat result,hist3;
    LUT(img1,lut,result);
    imshow("待匹配图像",img1);
    imshow("匹配的模板图像",img2);
    imshow("直方图匹配结果",result);
    calcHist(&result,1,channels,Mat(),hist3,1,bins,ranges);
    drawHist(hist3,NORM_L1,"hist3");
    waitKey(0);
}

void mubanpipei()//在大图像中找到和小图像一致的区域
{
    Mat img=imread("/home/zhanjiabin/桌面/task/study/test.jpg");
    Mat temp=imread("/home/zhanjiabin/桌面/task/study/testp.jpg");

    Mat result;

    matchTemplate(img,temp,result,TM_CCOEFF_NORMED);//模板匹配函数(输入图，模板图，输出图，匹配方法标志)
    double maxVal,minVal;

    Point maxLoc,minLoc;

    minMaxLoc(result,&minVal,&maxVal,&minLoc,&maxLoc);//寻找最大最小值函数(输入图，最小值，最大值，最小值所在坐标，最大值所在坐标)

    rectangle(img,Point(maxLoc.x,maxLoc.y),Point(maxLoc.x+temp.cols,maxLoc.y+temp.rows),Scalar(0,0,255),2);
                     //左上角                    //右下角  

    imshow("img",img);
    imshow("模板",temp);
    imshow("result",result);

    waitKey(0);

}